package com.yjk.dzpoke.websocket.bean;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.util.*;
import java.util.stream.Collector;
import java.util.stream.Collectors;

@Data
@AllArgsConstructor
@NoArgsConstructor
public class PokeType implements Comparable<PokeType> {

    public static void main(String[] args) {
        int c = 0;
        while (true) {
            List<Poke> pokes = Poke.NewPokes();
            List<Poke> list = Poke.getPoke(pokes, 7);
            PokeType t = getType(list, new ArrayList<>());
            if (t.type == 9 || t.type == 10) {
                list.sort(Poke::compareTo);
                System.out.println(list);
                System.out.println(getType(list, new ArrayList<>()));
                break;
            }
            c++;
        }
        System.out.println("发了" + c + "次发到同花顺");
        for (int i = 0; i < 15; i++) {
            List<Poke> pokes = Poke.NewPokes();
            List<Poke> list = Poke.getPoke(pokes, 7);
            list.sort(Poke::compareTo);
            System.out.println(list);
            System.out.println(getType(list, new ArrayList<>()));
        }

    }

    /**
     * 10皇家同花顺 9同花顺 8四条 7葫芦 6同花 5顺子 4三条 3两队 2一对 1高牌
     */
    private Integer type;
    /**
     * 点数
     */
    private Integer[] point;

    @Override
    public String toString() {
        StringBuffer sb = new StringBuffer();
        switch (type) {
            case 10:
                sb.append("皇家同花顺!无敌了~");
                break;
            case 9:
                sb.append("同花顺");
                break;
            case 8:
                sb.append("四条");
                break;
            case 7:
                sb.append("葫芦");
                break;
            case 6:
                sb.append("同花");
                break;
            case 5:
                sb.append("顺子");
                break;
            case 4:
                sb.append("三条");
                break;
            case 3:
                sb.append("两对");
                break;
            case 2:
                sb.append("一对");
                break;
            case 1:
            default:
                sb.append("高牌");
        }
        if (type != 10) {
            sb.append(" 点数:");
            for (Integer i : point) {
                if (i <= 10) {
                    sb.append(i + " ");
                } else {
                    switch (i) {
                        case 11:
                            sb.append("J ");
                            break;
                        case 12:
                            sb.append("Q ");
                            break;
                        case 13:
                            sb.append("K ");
                            break;
                        case 14:
                            sb.append("A ");
                            break;
                    }
                }
            }
        }
        return sb.toString();
    }

    public static PokeType getType(List<Poke> hand, List<Poke> pblic) {
        if (isFlush(hand, pblic)) {
            if (isRoyalFlush(hand, pblic))
                return new PokeType(10, new Integer[]{0});
            if (isStraightFlush(hand, pblic))
                return new PokeType(9, straightFlush(hand, pblic));
            return new PokeType(6, flush(hand, pblic));
        }
        if (isStraight(hand, pblic)) {
            return new PokeType(5, straight(hand, pblic));
        }
        if (isFour(hand, pblic)) {
            return new PokeType(8, four(hand, pblic));
        }
        if (isTwo(hand, pblic)) {
            return new PokeType(3, two(hand, pblic));
        }

        boolean three = isThree(hand, pblic);
        boolean one = isOne(hand, pblic);
        if (three && one) {
            return new PokeType(7, fullHouse(hand, pblic));
        } else if (three) {
            return new PokeType(4, three(hand, pblic));
        } else if (one) {
            return new PokeType(2, one(hand, pblic));
        }

        return new PokeType(1, none(hand, pblic));
    }


    private static Integer[] none(List<Poke> hand, List<Poke> pblic) {
        Poke poke = hand.stream().max(Comparator.comparing(Poke::getPoint)).get();
        return new Integer[]{poke.getPoint()};
    }


    private static Integer[] fullHouse(List<Poke> hand, List<Poke> pblic) {
        Integer[] three = three(hand, pblic);
        Integer[] one = one(hand, pblic);
        return new Integer[]{three[0], one[0]};
    }

    private static Integer[] straightFlush(List<Poke> hand, List<Poke> pblic) {
        return flush(hand, pblic);
    }

    private static boolean isStraightFlush(List<Poke> hand, List<Poke> pblic) {
        List<Poke> all = all(hand, pblic);
        int color = -1;
        Map<Integer, Long> map = all.stream().collect(Collectors.groupingBy(Poke::getColor, Collectors.counting()));
        for (Map.Entry<Integer, Long> entry : map.entrySet()) {
            Long count = entry.getValue();
            if (count >= 5) color = entry.getKey();
        }
        List<Integer> points = all.stream().map(Poke::getPoint).sorted().collect(Collectors.toList());
        if (all.contains(new Poke(color, points.get(0))) &&
                all.contains(new Poke(color, points.get(0) + 1)) &&
                all.contains(new Poke(color, points.get(0) + 2)) &&
                all.contains(new Poke(color, points.get(0) + 3)) &&
                all.contains(new Poke(color, points.get(0) + 4)))
            return true;
        if (all.size() >= 6) {
            if (all.contains(new Poke(color, points.get(1))) &&
                    all.contains(new Poke(color, points.get(1) + 1)) &&
                    all.contains(new Poke(color, points.get(1) + 2)) &&
                    all.contains(new Poke(color, points.get(1) + 3)) &&
                    all.contains(new Poke(color, points.get(1) + 4)))
                return true;
        }
        if (all.size() == 7) {
            if (all.contains(new Poke(color, points.get(2))) &&
                    all.contains(new Poke(color, points.get(2) + 1)) &&
                    all.contains(new Poke(color, points.get(2) + 2)) &&
                    all.contains(new Poke(color, points.get(2) + 3)) &&
                    all.contains(new Poke(color, points.get(2) + 4)))
                return true;
        }
        return false;
    }

    public static boolean isFlush(List<Poke> hand, List<Poke> pblic) {
        List<Poke> all = all(hand, pblic);
        Map<Integer, Long> map = all.stream().collect(Collectors.groupingBy(Poke::getColor, Collectors.counting()));
        for (Map.Entry<Integer, Long> entry : map.entrySet()) {
            Long count = entry.getValue();
            if (count >= 5) return true;
        }
        return false;
    }

    private static Integer[] flush(List<Poke> hand, List<Poke> pblic) {
        int color = 0;
        List<Poke> all = all(hand, pblic);
        Map<Integer, Long> map = all.stream().collect(Collectors.groupingBy(Poke::getColor, Collectors.counting()));
        for (Map.Entry<Integer, Long> entry : map.entrySet()) {
            Long count = entry.getValue();
            if (count >= 5) color = entry.getKey();
        }
        int finalColor = color;
        Poke poke = all.stream().filter(a -> a.getColor() == finalColor).max(Comparator.comparing(Poke::getPoint)).get();
        return new Integer[]{poke.getPoint()};
    }

    public static boolean isStraight(List<Poke> hand, List<Poke> pblic) {
        List<Poke> all = all(hand, pblic);
        List<Integer> points = all.stream().map(Poke::getPoint).sorted().collect(Collectors.toList());
        Integer i0 = points.get(0);
        Integer i1 = points.get(1);
        Integer i2 = points.get(2);
        Integer i3 = points.get(3);
        Integer i4 = points.get(4);
        if ((i1 == i0 + 1) && (i2 == i0 + 2) && (i3 == i0 + 3) && (i4 == i0 + 4))
            return true;
        if (points.size() == 6) {
            Integer i5 = points.get(5);
            if ((i2 == i1 + 1) && (i3 == i1 + 2) && (i4 == i1 + 3) && (i5 == i1 + 4))
                return true;
        } else {
            Integer i5 = points.get(5);
            if ((i2 == i1 + 1) && (i3 == i1 + 2) && (i4 == i1 + 3) && (i5 == i1 + 4))
                return true;
            Integer i6 = points.get(6);
            if ((i3 == i2 + 1) && (i4 == i2 + 2) && (i5 == i2 + 3) && (i6 == i2 + 4))
                return true;
        }
        return false;
    }

    private static Integer[] straight(List<Poke> hand, List<Poke> pblic) {
        List<Poke> all = all(hand, pblic);
        List<Integer> points = all.stream().map(Poke::getPoint).sorted().collect(Collectors.toList());
        Integer i0 = points.get(0);
        Integer i1 = points.get(1);
        Integer i2 = points.get(2);
        Integer i3 = points.get(3);
        Integer i4 = points.get(4);
        if ((i1 == i0 + 1) && (i2 == i0 + 2) && (i3 == i0 + 3) && (i4 == i0 + 4))
            return new Integer[]{i4};
        if (points.size() == 6) {
            Integer i5 = points.get(5);
            if ((i2 == i1 + 1) && (i3 == i1 + 2) && (i4 == i1 + 3) && (i5 == i1 + 4))
                return new Integer[]{i5};
        } else {
            Integer i5 = points.get(5);
            if ((i2 == i1 + 1) && (i3 == i1 + 2) && (i4 == i1 + 3) && (i5 == i1 + 4))
                return new Integer[]{i5};
            Integer i6 = points.get(6);
            if ((i3 == i2 + 1) && (i4 == i2 + 2) && (i5 == i2 + 3) && (i6 == i2 + 4))
                return new Integer[]{i6};
        }
        return new Integer[]{0};
    }

    public static boolean isRoyalFlush(List<Poke> hand, List<Poke> pblic) {
        List<Poke> all = all(hand, pblic);
        int color = -1;
        Map<Integer, Long> map = all.stream().collect(Collectors.groupingBy(Poke::getColor, Collectors.counting()));
        for (Map.Entry<Integer, Long> entry : map.entrySet()) {
            Long count = entry.getValue();
            if (count >= 5) color = entry.getKey();
        }
        return all.contains(new Poke(color, 10)) &&
                all.contains(new Poke(color, 11)) &&
                all.contains(new Poke(color, 12)) &&
                all.contains(new Poke(color, 13)) &&
                all.contains(new Poke(color, 14));
    }

    public static boolean isFour(List<Poke> hand, List<Poke> pblic) {
        List<Poke> all = all(hand, pblic);
        Map<Integer, Long> map = all.stream().collect(Collectors.groupingBy(Poke::getPoint, Collectors.counting()));
        for (Map.Entry<Integer, Long> entry : map.entrySet()) {
            Long count = entry.getValue();
            if (count == 4) return true;
        }
        return false;
    }

    private static Integer[] four(List<Poke> hand, List<Poke> pblic) {
        int p = 0;
        List<Poke> all = all(hand, pblic);
        Map<Integer, Long> map = all.stream().collect(Collectors.groupingBy(Poke::getPoint, Collectors.counting()));
        for (Map.Entry<Integer, Long> entry : map.entrySet()) {
            Long count = entry.getValue();
            if (count == 4) p = entry.getKey();
        }
        return new Integer[]{p};
    }

    public static boolean isThree(List<Poke> hand, List<Poke> pblic) {
        List<Poke> all = all(hand, pblic);
        Map<Integer, Long> map = all.stream().collect(Collectors.groupingBy(Poke::getPoint, Collectors.counting()));
        for (Map.Entry<Integer, Long> entry : map.entrySet()) {
            Long count = entry.getValue();
            if (count == 3) return true;
        }
        return false;
    }

    private static Integer[] three(List<Poke> hand, List<Poke> pblic) {
        int p = 0;
        List<Poke> all = all(hand, pblic);
        Map<Integer, Long> map = all.stream().collect(Collectors.groupingBy(Poke::getPoint, Collectors.counting()));
        for (Map.Entry<Integer, Long> entry : map.entrySet()) {
            Long count = entry.getValue();
            if (count == 3) p = entry.getKey();
        }
        return new Integer[]{p};
    }

    private static boolean isOne(List<Poke> hand, List<Poke> pblic) {
        List<Poke> all = all(hand, pblic);
        Map<Integer, Long> map = all.stream().collect(Collectors.groupingBy(Poke::getPoint, Collectors.counting()));
        for (Map.Entry<Integer, Long> entry : map.entrySet()) {
            Long count = entry.getValue();
            if (count == 2) return true;
        }
        return false;
    }

    private static Integer[] one(List<Poke> hand, List<Poke> pblic) {
        List<Poke> all = all(hand, pblic);
        int p = 0;
        Map<Integer, Long> map = all.stream().collect(Collectors.groupingBy(Poke::getPoint, Collectors.counting()));
        for (Map.Entry<Integer, Long> entry : map.entrySet()) {
            Long count = entry.getValue();
            if (count == 2) p = entry.getKey();
        }
        return new Integer[]{p};
    }

    public static boolean isTwo(List<Poke> hand, List<Poke> pblic) {
        int i = 0;
        List<Poke> all = all(hand, pblic);
        Map<Integer, Long> map = all.stream().collect(Collectors.groupingBy(Poke::getPoint, Collectors.counting()));
        for (Map.Entry<Integer, Long> entry : map.entrySet()) {
            Long count = entry.getValue();
            if (count == 2) i++;
        }
        return i == 2;
    }

    private static Integer[] two(List<Poke> hand, List<Poke> pblic) {
        int p1 = 0;
        int p2 = 0;
        List<Poke> all = all(hand, pblic);
        Map<Integer, Long> map = all.stream().collect(Collectors.groupingBy(Poke::getPoint, Collectors.counting()));
        for (Map.Entry<Integer, Long> entry : map.entrySet()) {
            Long count = entry.getValue();
            if (count == 2) {
                if (p1 == 0) {
                    p1 = entry.getKey();
                } else {
                    if (p1 > entry.getKey()) {
                        p2 = entry.getKey();
                    } else {
                        p2 = p1;
                        p1 = entry.getKey();
                    }
                }
            }
        }
        return new Integer[]{p1, p2};
    }

    public static List<Poke> all(List<Poke> hand, List<Poke> pblic) {
        List<Poke> all = new ArrayList<>();
        all.addAll(hand);
        all.addAll(pblic);
        return all;
    }

    @Override
    public int compareTo(PokeType that) {
        if (that.type == this.type) {
            for (int i = 0; i < point.length; i++) {
                if (this.point[i] != that.point[i]) {
                    return this.point[i] - that.point[i];
                }
            }
        } else return this.type - that.type;
        return 0;
    }
}
